const Util = require("./util.js");
const sessionStorageKey = '__SESSION__KEY__';
let sessionId = wx.getStorageSync(sessionStorageKey);
const loginQueue = [];
let isLoginning = false;

const req = {
  apiUrl: '',
  code2sessionId: null,
  isSessionAvailable: null,
  sessionHeaderKey: 'sessionId',
  init(opt = {}) {
    const {
      apiUrl,
      code2sessionId,
      isSessionAvailable,
      sessionHeaderKey,
    } = opt;
    if (apiUrl) {
      req.apiUrl = apiUrl;
    }
    if (code2sessionId) {
      req.code2sessionId = code2sessionId;
    }
    if (isSessionAvailable) {
      req.isSessionAvailable = isSessionAvailable;
    }
    if (sessionHeaderKey) {
      req.sessionHeaderKey = sessionHeaderKey;
    }
  },
};

/**
 * 判断请求状态是否成功
 * @param {number} status
 */
function isHttpSuccess(status) {
  return (status >= 200 && status < 300) || status === 304;
}

/**
 * promise请求
 * @param {object} options {}
 */
/*
 *	@desc 这是一个用于弥补wx.request问题的，特别是数据上报。会有很多接口调用。
 *   wx.request不允许一次发多于5个的请求。这是一个全局的。
 *   因此，这个module也是全局的。
 */

/*
 *	@desc 先取得一个全局的__app__对象。
 */
var __app__ = wx;
/*
 *	@desc 设置当前最大 的请求数
 */
__app__.MAX_REQUEST_COUNT = __app__.MAX_REQUEST_COUNT || 5;
/*
 *	@desc 记录当前有多少个请求正在Running，以计算有多少个可以出列。
 */
__app__.currentRunning = 0;
/*
 *	@desc 记录请求队列，它并不限制请求大小。
 */
__app__.requestQueue = [];
/*
 *	@desc 定义一下请求队列的每一项结构。
 */
var requestQueueItem = {
  context: __app__, //请求可能需要保存的context
  reqData: null //请求用的真实数据。
};

/**
 * 
 * @desc 封装那个5个限制的请求。
 * @doc https://mp.weixin.qq.com/debug/wxadoc/dev/api/network-request.html#wxrequestobject
 * @param {Object} data 
 */

function requestP(options = {}) {
  const {
    success,
    fail,
  } = options;
  // 统一注入约定的header
  const header = Object.assign({
    [req.sessionHeaderKey]: sessionId,
  }, options.header);

  return new Promise((res, rej) => {
    /*
     *	@desc 克隆一下请求队列的Item
     */
    var rqi = Object.create(requestQueueItem);
    rqi.reqData = options;

    /*
     *	@desc 把请求入队。不论是什么状态。生产者
     */
    __app__.requestQueue.push(rqi);
    /*
     *	@desc 定义消费这个队列的消费者,消费者可以是在多个页面里。
     */
    var comsumer = function() {
      if (__app__.currentRunning < __app__.MAX_REQUEST_COUNT) {
        __app__.currentRunning++;
        var _qdata = __app__.requestQueue.shift();
        var wxRequestData = _qdata.reqData;

        var tempComplete = wxRequestData.complete;
        wxRequestData.complete = function(res) {
          tempComplete && tempComplete.call(rqi.context, res);
          // 处理request:fail的客户端bug
          if (res.errMsg && /^request:fail/.test(res.errMsg)) {
            Util.handleRequestError(res);
          }
          __app__.currentRunning--;

          /*
           *	@desc 如果有一个请求没完。则由上一个请求重新发起。
           */
          if (__app__.requestQueue.length > 0) {
            comsumer.call(__app__);
          }
        }
        wx.request(Object.assign({},
          wxRequestData, {
            header,
            success(r) {
              const isSuccess = isHttpSuccess(r.statusCode);
              if (isSuccess) { // 成功的请求状态
                if (success) {
                  success(r.data);
                  return;
                }
                res(r.data);
              } else {
                const err = {
                  msg: `服务器好像出了点小问题，请与客服联系~（错误代码：${r.statusCode}）`,
                  detail: r,
                };
                if (fail) {
                  fail(err);
                  return;
                }
                rej(err);
              }
            },
            fail(err) {
              if (fail) {
                fail(err);
                return;
              }
              rej(err);
            }
          },
        ));
      }
    }
    /*
     *	@desc 开始消费
     */
    comsumer();
  });
}

/**
 * 登录
 */
function login() {
  return new Promise((res, rej) => {
    // 微信登录
    wx.login({
      success(r1) {
        if (r1.code) {
          // 获取sessionId
          if (!req.code2sessionId) {
            rej('code2sessionId未定义');
            return;
          }
          req.code2sessionId(r1.code)
            .then((r2) => {
              const newSessionId = r2;
              sessionId = newSessionId; // 更新sessionId
              // 保存sessionId
              wx.setStorage({
                key: sessionStorageKey,
                data: newSessionId,
              });
              res(r2);
            })
            .catch(rej);
        } else {
          rej(r1);
        }
      },
      fail: rej,
    });
  });
}

/**
 * 获取sessionId
 */
function getSessionId() {
  return new Promise((res, rej) => {
    // 本地sessionId丢失，重新登录
    if (!sessionId) {
      loginQueue.push({
        res,
        rej
      });

      if (!isLoginning) {
        isLoginning = true;

        login()
          .then((r1) => {
            isLoginning = false;
            while (loginQueue.length) {
              loginQueue.shift().res(r1);
            }
          })
          .catch((err) => {
            isLoginning = false;
            while (loginQueue.length) {
              loginQueue.shift().rej(err);
            }
          });
      }
    } else {
      res(sessionId);
    }
  });
}

/**
 * ajax高级封装
 * @param {object} options {}
 * @param {boolean} keepLogin true
 */
function request(options = {}, keepLogin = true) {
  if (keepLogin) {
    return new Promise((res, rej) => {
      getSessionId()
        .then(() => {
          // 获取sessionId成功之后，发起请求
          requestP(options)
            .then((r2) => {
              if (!req.isSessionAvailable) {
                rej('isSessionAvailable未定义');
                return;
              }
              if (!req.isSessionAvailable(r2)) {
                /**
                 * 登录状态无效，则重新走一遍登录流程
                 * 销毁本地已失效的sessionId
                 */
                sessionId = '';
                getSessionId()
                  .then(() => {
                    requestP(options)
                      .then(res)
                      .catch(rej);
                  })
                  .catch(rej);
              } else {
                res(r2);
              }
            })
            .catch(rej);
        })
        .catch(rej);
    });
  }
  // 不需要sessionId，直接发起请求
  return requestP(options);
}

/**
 * 插件接口
 * @param {object} plugin
 */
function use(plugin) {
  return plugin.install(req, request);
}

req.use = use;

module.exports = req;